﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CacheProvider.AppFabric;
using System.Collections.Specialized;
using System.Threading;

namespace AppFabricCacheProviderUnitTests
{
    public partial class AppFabricCacheProviderTest
    {

        [TestMethod]
        public void AddSameKeyInMultipleThreads()
        {
            int counter = 0;
            string key = "TestKey";
            do
            {
                //Schedule two threads at the same time to add an item to the cache with the same key
                Timer timer1 = new Timer(AddCacheEntryCallback, "Test Value Thread 1", 10, Timeout.Infinite);
                Timer timer2 = new Timer(AddCacheEntryCallback, "Test Value Thread 2", 10, Timeout.Infinite);
                Thread.Sleep(5 * 1000);//wait for the threads to finish
                var valueFromCache = target.Get<string>(key);
                Assert.IsNotNull(valueFromCache);
                Assert.IsTrue(valueFromCache.Equals("Test Value Thread 1") || valueFromCache.Equals("Test Value Thread 2"));
                target.Remove(key);
            } while (counter++ < 10);

            //clean-up
            target.Remove(key);
        }


        [TestMethod]
        public void PutWithDependencyInMultipleThreads()
        {
            ManualResetEvent waitHandle1 = new ManualResetEvent(false);
            ManualResetEvent waitHandle2 = new ManualResetEvent(false);
            ManualResetEvent waitHandle3 = new ManualResetEvent(false);
            ManualResetEvent waitHandle4 = new ManualResetEvent(false);
            ManualResetEvent waitHandle5 = new ManualResetEvent(false);


            Timer timer1 = new Timer(
                                        state =>
                                        {
                                            try
                                            {
                                                this.PutWithSqlDependency("Test Key: " + Thread.CurrentThread.ManagedThreadId);
                                            }
                                            finally
                                            {
                                                (state as ManualResetEvent).Set();
                                            }
                                        },
                                        waitHandle1,
                                        10,
                                        Timeout.Infinite);

            Timer timer2 = new Timer(
                                        state =>
                                        {
                                            try
                                            {
                                                this.PutWithSqlDependency("Test Key: " + Thread.CurrentThread.ManagedThreadId);
                                            }
                                            finally
                                            {
                                                (state as ManualResetEvent).Set();
                                            }
                                        },
                                        waitHandle2,
                                        10,
                                        Timeout.Infinite);

            Timer timer3 = new Timer(
                                        state =>
                                        {
                                            try
                                            {
                                                this.PutWithSqlDependency("Test Key: " + Thread.CurrentThread.ManagedThreadId);
                                            }
                                            finally
                                            {
                                                (state as ManualResetEvent).Set();
                                            }
                                        },
                                        waitHandle3,
                                        10,
                                        Timeout.Infinite);


            Timer timer4 = new Timer(
                                        state =>
                                        {
                                            try
                                            {
                                                this.GetTest_Generic();
                                            }
                                            finally
                                            {
                                                (state as ManualResetEvent).Set();
                                            }
                                        },
                                        waitHandle4,
                                        10,
                                        Timeout.Infinite);

            Timer timer5 = new Timer(
                                        state =>
                                        {
                                            try
                                            {
                                                this.GetObjectsByAllTagsTest_Region();
                                            }
                                            finally
                                            {
                                                (state as ManualResetEvent).Set();
                                            }
                                        },
                                        waitHandle5,
                                        10,
                                        Timeout.Infinite);


            waitHandle1.WaitOne();
            waitHandle2.WaitOne();
            waitHandle3.WaitOne();
            waitHandle4.WaitOne();
            waitHandle5.WaitOne();
        }


        private void AddCacheEntryCallback(Object state)
        {
            try
            {
                string key = "TestKey";
                string value = state as string;
                bool expected = true;
                bool actual;

                actual = target.Add(key, value);
                Assert.AreEqual(expected, actual);
                string valueFromCache = target.Get<String>(key);
                Assert.IsTrue(valueFromCache.Equals(value));
            }
            catch (Exception)
            {
                //swallow the exception                
            }
        }

        [TestMethod()]
        public void NamedAndDefaultCacheInConcurrentThreads()
        {
            string key = "TestKey";

            //Thread with 
            Timer timer1 = new Timer(
                                    state =>
                                    {
                                        try
                                        {
                                            target.Put(key, state);
                                            var actual = target.Get<string>(key);
                                            Assert.AreEqual(actual, state);
                                        }
                                        finally
                                        {
                                            target.Remove(key);
                                        }
                                    },
                                    "Test Value1",
                                    10,
                                    Timeout.Infinite);

            Timer timer2 = new Timer(
                    state =>
                    {
                        try
                        {
                            target.SetNamedCache("TestNamedCache");
                            target.Put(key, state);
                            var actual = target.Get<string>(key);
                            Assert.AreEqual(actual, state);
                        }
                        finally
                        {
                            target.Remove(key);
                            target.SetDefaultCache();
                        }
                    },
                    "Test Value2",
                    10,
                    Timeout.Infinite);


            Thread.Sleep(5 * 1000);           
        }
    }
}
